//bls 3

datablock fxDtsBrickData(LogicGate_ClientIOBrick_Data){
	brickFile = $LuaLogic::Path @ "bricks/blb/serialport.blb";
	iconName = $LuaLogic::Path @ "icons/serialport";
	
	category = "Logic Bricks";
	subCategory = "Special I/O";
	uiName = "Client I/O Brick";
	logicUIName = "Client I/O Brick";
	logicUIDesc = "Allows clients to connect and share data with logic bricks.";
	orientationFix = 2;
	hasPrint = 0;
	
	isLogic = true;
	isLogicGate = true;
	isLogicInput = true;
	
	logicInit   = lualogic_readfile($LuaLogic::Path @ "bricks/inputs/iobrick-init.lua"  );
	logicInput  = lualogic_readfile($LuaLogic::Path @ "bricks/inputs/iobrick-input.lua" );
	logicUpdate = lualogic_readfile($LuaLogic::Path @ "bricks/inputs/iobrick-update.lua");
	//logicGlobal = lualogic_readfile($LuaLogic::Path @ "bricks/inputs/iobrick-global.lua");
	
	numLogicPorts = 18;
	
	logicPortPos[ 0] = "-8 1 -3";
	logicPortPos[ 1] = "-6 1 -3";
	logicPortPos[ 2] = "-4 1 -3";
	logicPortPos[ 3] = "-2 1 -3";
	logicPortPos[ 4] =  "0 1 -3";
	logicPortPos[ 5] =  "2 1 -3";
	logicPortPos[ 6] =  "4 1 -3";
	logicPortPos[ 7] =  "6 1 -3";
	
	logicPortPos[16] =  "-8 1 -3";
	
	logicPortPos[ 8] = "-8 -1 -3";
	logicPortPos[ 9] = "-6 -1 -3";
	logicPortPos[10] = "-4 -1 -3";
	logicPortPos[11] = "-2 -1 -3";
	logicPortPos[12] =  "0 -1 -3";
	logicPortPos[13] =  "2 -1 -3";
	logicPortPos[14] =  "4 -1 -3";
	logicPortPos[15] =  "6 -1 -3";
	
	logicPortPos[17] = "-8 -1 -3";
	
	logicPortType[0] = 1;
	logicPortDir[0] = 1;
	logicPortUIName[0] = "In0";
	
	logicPortType[1] = 1;
	logicPortDir[1] = 1;
	logicPortUIName[1] = "In1";
	
	logicPortType[2] = 1;
	logicPortDir[2] = 1;
	logicPortUIName[2] = "In2";
	
	logicPortType[3] = 1;
	logicPortDir[3] = 1;
	logicPortUIName[3] = "In3";
	
	logicPortType[4] = 1;
	logicPortDir[4] = 1;
	logicPortUIName[4] = "In4";
	
	logicPortType[5] = 1;
	logicPortDir[5] = 1;
	logicPortUIName[5] = "In5";
	
	logicPortType[6] = 1;
	logicPortDir[6] = 1;
	logicPortUIName[6] = "In6";
	
	logicPortType[7] = 1;
	logicPortDir[7] = 1;
	logicPortUIName[7] = "In7";
	
	logicPortType[8] = 0;
	logicPortDir[8] = 3;
	logicPortUIName[8] = "Out0";
	
	logicPortType[9] = 0;
	logicPortDir[9] = 3;
	logicPortUIName[9] = "Out1";
	
	logicPortType[10] = 0;
	logicPortDir[10] = 3;
	logicPortUIName[10] = "Out2";
	
	logicPortType[11] = 0;
	logicPortDir[11] = 3;
	logicPortUIName[11] = "Out3";
	
	logicPortType[12] = 0;
	logicPortDir[12] = 3;
	logicPortUIName[12] = "Out4";
	
	logicPortType[13] = 0;
	logicPortDir[13] = 3;
	logicPortUIName[13] = "Out5";
	
	logicPortType[14] = 0;
	logicPortDir[14] = 3;
	logicPortUIName[14] = "Out6";
	
	logicPortType[15] = 0;
	logicPortDir[15] = 3;
	logicPortUIName[15] = "Out7";
	
	logicPortType[16] = 1;
	logicPortDir[16] = 0;
	logicPortUIName[16] = "InClk";
	logicPortCauseUpdate[16] = true;
	
	logicPortType[17] = 0;
	logicPortDir[17] = 0;
	logicPortUIName[17] = "OutClk";
	logicPortCauseUpdate[17] = true;
};

// Input
function serverCmdLogicIO(%client, %data) {
	if(isObject(%brick = %client.LogicIO_Brick)) {
		%brick.LogicIO_input(%data);
	}
}
function fxDtsBrick::LogicIO_input(%brick, %data) {
	lualogic_sendInput(%brick, 1, %data);
}

// Output
function fxDtsBrick::LogicIO_output(%brick, %data) {
	if(isObject(%client = %brick.LogicIO_Client)) {
		%maxLen = 250;
		%len = strLen(%data);
		
		commandToClient(%client, 'LogicIO', getSubStr(%data, 0, %maxLen));
		
		//if(%len > %maxLen) {
		//	%brick.LogicIO_output(getSubStr(%data, 0, %maxLen));
		//	%brick.LogicIO_output(getSubStr(%data, %maxLen, %len - %maxLen));
		//} else {
		//	commandToClient(%client, 'LogicIO', %data);
		//}
	}
}
function LogicGate_ClientIOBrick_Data::LuaLogic_Callback(%db, %brick, %data) {
	%brick.LogicIO_output(%data);
}

// Binding
function LogicGate_ClientIOBrick_Data::Logic_OnInput(%db, %brick, %pos, %norm, %client){
	if(isObject(%client)) {
		if(%client.LogicIO_Brick == %brick) {
			%client.LogicIO_unbind();
		} else {
			%brick.LogicIO_bindClient(%client);
		}
	}
}
function GameConnection::LogicIO_unbind(%client) {
	if(isObject(%brick = %client.LogicIO_Brick)) %brick.LogicIO_unbindClient(%brick);
}
function fxDtsBrick::LogicIO_unbindClient(%brick) {
	if(isObject(%client = %brick.LogicIO_Client)) {
		%client.LogicIO_Brick = "";
		messageClient(%client, 0, "\c6Logic I/O: Unbound from \c3" @ %brick);
		if(isObject(%player = %client.player)) serverPlay3D(LogicIO_PlugOutSound, %player.getHackPosition());
	}
	%brick.LogicIO_Client = "";
	serverPlay3D(LogicIO_PlugOutSound, %brick.getPosition());
	
	//SerialLink_delete(%brick.LogicIO_bindLink);
	//%brick.LogicIO_bindLink = "";
}
function fxDtsBrick::LogicIO_bindClient(%brick, %client) {
	%brick.LogicIO_unbindClient();
	%client.LogicIO_unbind();
	
	%client.LogicIO_Brick = %brick;
	%brick.LogicIO_Client = %client;
	messageClient(%client, 0, "\c6Logic I/O: Bound to \c3" @ %brick);
	
	serverPlay3D(LogicIO_PlugInSound, %player.getHackPosition());
	serverPlay3D(LogicIO_PlugInSound, %brick.getPosition());
	
	//%linkPos = vectorAdd(%brick.getPosition(), LogicIO_rotateVector("0 2 0", %brick.angleId));
	//%linkRot = "0 0 1" SPC ($math.pi/2*%brick.angleId);
	//%brick.LogicIO_bindLink = SerialLink_create(%linkPos, %linkRot, %client.player);
}

// Util
function LogicIO_rotateVector(%vector, %steps) {
	switch(%steps % 4) {
		case 0: return %vector;
		case 1: return  getWord(%vector, 1) SPC -getWord(%vector, 0) SPC getWord(%vector, 2);
		case 2: return -getWord(%vector, 0) SPC -getWord(%vector, 1) SPC getWord(%vector, 2);
		case 3: return -getWord(%vector, 1) SPC  getWord(%vector, 0) SPC getWord(%vector, 2);
	}
}

// Serial link visuals

datablock AudioProfile(LogicIO_PlugInSound) {
	filename    = "./plugIn.wav";
	description = MediumClose3D;
	preload     = true;
};

datablock AudioProfile(LogicIO_PlugOutSound) {
	filename    = "./plugOut.wav";
	description = MediumClose3D;
	preload     = true;
};

function SerialLink_delete(%link) {
	if(!isObject(%link)) return;
	cancel(%link.updateSched);
	%link.end0.delete();
	%link.end1.delete();
	%link.wire.delete();
}
function SerialLink_createEnd() {
	%shape = new StaticShape() {
		// todo
	};
	return %shape;
}
function SerialLink_createPlayerEnd() {
	%shape = new StaticShape() {
		// todo
	};
	return %shape;
}
function SerialLink_createWire() {
	%shape = new StaticShape() {
		// todo
	};
	return %shape;
}
function SerialLink_create(%pos1, %rot1, %player) {
	%link = new ScriptObject();
	%link.end0 = SerialLink_createEnd();
	%link.end1 = SerialLink_createPlayerEnd();
	%link.wire = SerialLink_createWire();
	%link.endPlayer0 = %player;
	SerialLink_update(%link);
}
function SerialLink_moveEndToPlayer(%shape, %player) {
	%shape.setTransform(%player.getSlotTransform(2)); // todo
}
function SerialLink_moveWire(%shape, %pos1, %pos2) {
	// todo
	%midPos = vectorScale(vectorAdd(%pos1, %pos2), 0.5);
	%shape.setTransform(%midPos SPC "0 0 1 0");
}
function SerialLink_update(%link) {
	cancel(%link.updateSched);
	for(%i=0; %i<=1; %i++) {
		%player = %link.endPlayer[%i];
		if(%player) {
			//if(!isObject(%player) || %player.getState() $= "Dead") {
			if(!isObject(%player)) {
				if(isObject(%client = %player.client)) %client.LogicIO_Unbind();
				SerialLink_delete(%link);
				return;
			} else {
				SerialLink_moveEndToPlayer(%link.end[%i], %player);
				%moved = 1;
			}
		}
	}
	if(%moved) {
		%dist = vectorLen(vectorSub(%pos1, %pos2));
		if(%dist > $SerialLink_maxDistance) { // If link is stretched too long, break it
			for(%i=0; %i<=1; %i++) { // Unbind client(s)
				if(isObject(%player = %link.endPlayer[%i]) && isObject(%client = %player.client)) {
					%client.LogicIO_Unbind();
				}
			}
			SerialLink_delete(%link); // Delete link
			return;
		} else { // Link moved, not too stretched -> update position
			SerialLink_moveWire(%link.wire, %link.end[0].getPosition(), %link.end[1].getPosition());
		}
	}
	%link.updateSched = schedule(32, 0, SerialLink_update, %link);
}
